package cn.gogpay.dcb.ixc.cloud.auth.listener;

import cn.gogpay.dcb.ixc.cloud.auth.annotation.ApiAdmin;
import cn.gogpay.dcb.ixc.cloud.auth.annotation.EnableCloudAuth;
import cn.gogpay.dcb.ixc.cloud.auth.dto.AppDTO;
import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.env.Environment;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * @author: YinHeng
 * @date: 2021/2/7 11:08
 * @description: 容器初始化完成监听器，收集应用实例名称和根路径、以及controller接口信息发送到管理后台
 */
public class CmdRunner implements CommandLineRunner, ApplicationContextAware {

    private static final Logger logger = LoggerFactory.getLogger(CmdRunner.class);

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Resource(name = "authTemplate")
    private RestTemplate restTemplate;

    /**
     * 应用信息注册接口默认地址，如有变动，可在本地配置文件中配置进行覆盖。
     */
    @Value("${cloud.auth.register.url:http://admin-middle-backend/sysApp/register}")
    private String registerUrl;

    @Override
    public void run(String... args) {
        logger.info("cloud-auth 开始收集接口...");
        Environment environment = applicationContext.getEnvironment();

        //设置应用信息
        AppDTO appDTO = new AppDTO();
        appDTO.setContextPath(environment.getProperty("server.servlet.context-path"));
        appDTO.setCode(environment.getProperty("spring.application.name"));
        Class<?> mainApplicationClass = deduceMainApplicationClass();
        EnableCloudAuth enableCloudAuth = mainApplicationClass.getAnnotation(EnableCloudAuth.class);
        if (enableCloudAuth != null) {
            appDTO.setName(enableCloudAuth.appName());
        }

        //api接口集合
        Set<AppDTO.ApiDTO> apiSet = new HashSet<>();

        //从容器中获取请求映射处理类
        RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
        Map<RequestMappingInfo, HandlerMethod> handlerMethods = mapping.getHandlerMethods();

        //遍历请求与其关联的controller方法
        for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : handlerMethods.entrySet()) {
            Method method = entry.getValue().getMethod();
            //判断controller方法上有没有指定的注解
            ApiAdmin annotation = method.getAnnotation(ApiAdmin.class);
            if (annotation != null) {

                //获取requestMapping中的url和请求方法
                Set<String> urls = entry.getKey().getPatternsCondition().getPatterns();
                RequestMethodsRequestCondition requestMethodsRequestConditions = entry.getKey().getMethodsCondition();
                Set<RequestMethod> requestMethodSet = new HashSet<>();
                //如果接口没有限制请求方式，则设置支持所有请求方式
                if (requestMethodsRequestConditions == null || requestMethodsRequestConditions.getMethods().size() == 0) {
                    for (RequestMethod reqMethod : RequestMethod.values()) {
                        requestMethodSet.add(reqMethod);
                    }
                } else {
                    requestMethodSet = entry.getKey().getMethodsCondition().getMethods();
                }

                //生成api并存入集合
                for (String url : urls) {
                    AppDTO.ApiDTO apiDTO = new AppDTO.ApiDTO();
                    apiDTO.setUrl(url);
                    apiDTO.setMethods(requestMethodSet);
                    apiDTO.setName(annotation.name());
                    apiSet.add(apiDTO);
                }
            }
        }
        appDTO.setApiSet(apiSet);

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
        HttpEntity<String> formEntity = new HttpEntity<>(JSON.toJSONString(appDTO), headers);

        ParameterizedTypeReference<Object> typeReference = new ParameterizedTypeReference<Object>() {
        };

        try {
            logger.info("cloud-auth 请求参数：{}", JSON.toJSONString(appDTO));
            ResponseEntity<Object> responseEntity = restTemplate.exchange(registerUrl, HttpMethod.POST, formEntity, typeReference);
            logger.info("cloud-auth 应用注册结果：{}", JSON.toJSONString(responseEntity));
        } catch (Exception e) {
            logger.error("cloud-auth 应用注册推送异常...");
        }
    }

    /**
     * 通过遍历运行时方法栈获取方法名为“main”的方法所在的类
     *
     * @return
     */
    private Class<?> deduceMainApplicationClass() {
        try {
            StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
            for (StackTraceElement stackTraceElement : stackTrace) {
                if ("main".equals(stackTraceElement.getMethodName())) {
                    return Class.forName(stackTraceElement.getClassName());
                }
            }
        } catch (ClassNotFoundException ex) {
            // Swallow and continue
        }
        return null;
    }

}
